/*
============================================================================
 Name        : dd_stack.c
 Author      : Jian (raphyer@hotmail.com)
 Version     :
 Copyright   : GPL
 Description : dd_stack.c - source file
============================================================================
*/

#include "dd_stack.h"


#define __START_TOP     -1
#define __DEFAULT_SIZE  8


static void _extend(dd_stack* stack)
{
	void** preData	= stack->data ;
	DD_UINT preSize	= stack->size ;

	__DD_ASSERT( preData != DD_NULL || preSize == 0 ) ;

	stack->size += stack->granularity ;
	stack->data = (void**)DD_MALLOC(stack->size * sizeof(void*)) ;

	DD_MEMSET(stack->data, 0, stack->size * sizeof(void*));
	if ( preData && preSize != 0 )
		DD_MEMCPY(stack->data, preData, preSize * sizeof(void*)) ;
	if(preData != NULL) {
		DD_FREE(preData);
		preData = NULL;
	}
}

dd_stack* dd_stack_create(DD_UINT granularity) 
{
	dd_stack* stack = DD_MALLOC(sizeof(dd_stack)) ;
	DD_MEMSET(stack, 0, sizeof(dd_stack)) ;
	stack->granularity	= (granularity > 0) ? granularity : __DEFAULT_SIZE ;
	stack->top			= __START_TOP ;
	
	_extend(stack) ;

	return stack ;
}


DD_UINT dd_stack_count(const dd_stack* stack) 
{
	__DD_ASSERT(stack->top >= __START_TOP) ;

	return (stack->top + 1) ; 
}

void dd_stack_destroy(dd_stack* stack) 
{
	if (stack) {
		DD_FREE(stack->data) ;
		DD_FREE(stack) ;
	}
}


void dd_stack_push(dd_stack* stack, void* data) 
{
	stack->top++ ;

	if (stack->top == (DD_INT)stack->size)
		_extend(stack) ;

	stack->data[stack->top] = data ;
}


void* dd_stack_pop(dd_stack* stack) 
{
	void* ret = DD_NULL ;

	__DD_ASSERT_DEFENSE( stack->top > __START_TOP, return DD_NULL ) ;

	ret = stack->data[stack->top] ;
	stack->top-- ;

	return ret ;
}


void* dd_stack_top(const dd_stack* stack) 
{
	if (stack->top > __START_TOP)
		return stack->data[stack->top];

	return DD_NULL ;
}


void dd_stack_popn(dd_stack* stack, DD_INT n) 
{
    if (n >= 0 && n <= stack->top)
        stack->top -= n ;
}

void* dd_stack_datan(const dd_stack* stack, DD_INT index)
{
    if (index < 0 || index > stack->top )
        return DD_NULL ;

    return stack->data[stack->top - index] ;
}

void dd_stack_reset(dd_stack* stack) 
{
	stack->top = __START_TOP ;
}

DD_BOOL dd_stack_own_data(dd_stack* stack, void* data) 
{
	DD_INT i = 0 ;
	for (; i <= stack->top; i++) {
		if (stack->data[i] == data)
			return DD_TRUE ;
	}

	return DD_FALSE ;
}


#ifdef __DD_UNITTEST

DD_BOOL dd_stack_test() 
{
	dd_stack* stack = dd_stack_create(4) ;
	const DD_UINT testCount = 100 ;
	DD_UINT i = 0 ;

	/* Test push */

	for ( ; i < testCount; i++ ) {
		dd_stack_push(stack, (void*)i) ;
	}
	
	__DD_ASSERT_DEFENSE( dd_stack_count(stack) == testCount, return DD_FALSE ) ;

	__DD_ASSERT_DEFENSE( (DD_UINT)(dd_stack_top(stack)) == (testCount - 1), return DD_FALSE ) ;

	__DD_ASSERT_DEFENSE( dd_stack_count(stack) == testCount, return DD_FALSE ) ;

	/* Test pop */
	i = testCount - 1 ;

	for ( ; (DD_INT)i >= 0 ; i-- ) {
		__DD_ASSERT_DEFENSE(i == (DD_UINT)dd_stack_pop(stack), return DD_FALSE) ;
	}
	
	__DD_ASSERT_DEFENSE( dd_stack_count(stack) == 0, return DD_FALSE ) ;

	dd_stack_destroy(stack) ;

	return DD_TRUE ;

}

#endif  /* __DD_UNITTEST */

/* --------------------------- File End --------------------------- */ 
